#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>

#include "airkiss.h"
#include "vpaspcap.h"
#include "ieee80211.h"

static void systemd_checking();
static void switchchannel(int sockfd, struct timeval* start, struct timeval now, int akst, airkiss_context_t* ak);

char const* dev = "wlan0";
int main(int argc, char* argv[])
{
    int c;
    int post = 0;
    while ((c = getopt (argc, argv, "p")) != -1) {
        switch (c)
        {
        case 'p':
            post = 1;
            break;
        case '?':
            fprintf(stdout, "Usage: %s [-p] [interface]\n", argv[0]);
            return 1;
        default:
            fprintf(stderr, "Unkown option\n");
            return -1;
        }
    }
    if (optind < argc)
        dev = argv[optind];
    openlog("airkiss", LOG_CONS|LOG_NDELAY, 0);

    if (post) {
        // Get airkiss result
        // Wait interface get IP
        // info
        return 0;
    }

    // Airkiss process
    // 检查运行环境
    systemd_checking();

    // 端口设置，端口打开
    int sockfd = opensocket(dev);
    if (-1 == sockfd) {
        syslog(LOG_ERR, "Open interface %s for capture failed.", dev);
        return -1;
    }
    airkiss_context_t airkiss;
    const airkiss_config_t akconf = {
        (airkiss_memset_fn)&memset,
        (airkiss_memcpy_fn)&memcpy,
        (airkiss_memcmp_fn)&memcmp,
        0
    };
    if (airkiss_init(&airkiss, &akconf)) {
        syslog(LOG_ERR, "Init airkiss context failed.");
        return -1;
    }
    struct timeval start = {0}, now, tvf;
    int akst = AIRKISS_STATUS_CONTINUE;
    tvf.tv_sec = 0;
    tvf.tv_usec = AIRKISS_LEADING_TO * 1000;
    uint8_t rbuff[4096];
    size_t rsz = sizeof(rbuff);
    //
    while (1) {
        gettimeofday(&now, NULL);
        // 是否需要切换频道
        switchchannel(sockfd, &start, now, akst, &airkiss);

        // 抓包
        int rd = read_packet(sockfd, rbuff, rsz, &tvf);
        // 包长检查
        if (rd > sizeof(mac_t) + sizeof(rth_t)) {
            rth_t* rth = (rth_t*)rbuff;
            unsigned short rthl = le16toh(rth->it_len);
            if (rthl >= (rd - sizeof(mac_t))) { // Got a NOT radio frame?
                continue;
            }
            akst = airkiss_recv(&airkiss, &rbuff[rthl], rd - rthl);
            if (AIRKISS_STATUS_COMPLETE == akst) {
                break;
            }
        }
        else if (rd < 0) {
            syslog(LOG_ERR, "Read packet from RAW socket failed.");
            break;
        }
    }
    // 端口恢复
    closesocket(sockfd, dev);

    if (AIRKISS_STATUS_COMPLETE != akst) {
        syslog(LOG_ERR, "Airkiss fail.");
        // TODO: 通知配网失败, 并通知网络服务切换到 STA 模式
        return -1;
    }

    // Update wpa_supplicant.conf
    syslog(LOG_INFO, "Airkiss success, update wpa config.");
    // 写入
    airkiss_result_t result;
    if (airkiss_get_result(&airkiss, &result)) {
        syslog(LOG_ERR, "Get airkiss results error.\n");
        // TODO:
        return -2;
    }
    // 写 wpa 配置文件
    if (result.ssid_length > 0 && result.ssid_length <= 64) {
        char ssid[32] = {0};
        char psk[64] = {0};
        if (result.pwd_length > 0) memcpy(psk, result.pwd, result.pwd_length);
        memcpy(ssid, result.ssid, result.ssid_length);
        syslog(LOG_INFO, "Airkiss success, ssid: %s, psk: %s", ssid, psk);
        // TODO: 通知配网成功, 设置后续操作, 并通知网络服务切换到 STA 模式
    }
    else {
        // TODO:
    }
    return 0;
}

static int chidx = 0;
static uint8_t channelscanlist[] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
    0 // Terminator
};
static void switchchannel(int sockfd, struct timeval* start, struct timeval now, int akst, airkiss_context_t* ak)
{
    // 切换频道两个超时
    int sw = 0;
    long ts = (now.tv_sec - start->tv_sec) * 1000 + (now.tv_usec - start->tv_usec) / 1000;
    switch(akst) {
    case AIRKISS_STATUS_CONTINUE:
        if (ts > AIRKISS_LEADING_TO) sw = 1;
        break;
    case AIRKISS_STATUS_CHANNEL_LOCKED:
        if (ts > AIRKISS_PROCESS_TO) sw = 1;
        break;
    default:
        break;
    }
    if (sw) {
        start->tv_sec = now.tv_sec;
        start->tv_usec = now.tv_usec;
        if (0 == channelscanlist[chidx]) {
            chidx = 0;
        }
        set_channel(sockfd, dev, channelscanlist[chidx]);
        chidx++;
        airkiss_change_channel(ak);
    }
}

static int systemd_isactive(char const* service)
{
    char cmd[256];
    sprintf(cmd, "systemctl is-active %s.service", service);
    FILE* fp = popen(cmd, "r");
    if (!fp) {
        //perror("is-active");
        return 0;
    }
    size_t rd = fread(cmd, 1, sizeof(cmd), fp);
    pclose(fp);
    if (rd > 0 &&
            strncasecmp(cmd, "active", 6) == 0) {
        return 1;
    }
    //printf("isactive read = %lu\n", rd);
    return 0;
}
static void systemd_checking()
{
    // hostapd.service isc-dhcp-server.service isc-dhcp-server6.service wpa_supplicant.service
    if (systemd_isactive("hostapd")) {
        syslog(LOG_ERR, "Service hostapd.service still running");
        exit(-1);
    }
    if (systemd_isactive("isc-dhcp-server")) {
        syslog(LOG_ERR, "Service isc-dhcp-server.service still running");
        exit(-1);
    }
    if (systemd_isactive("isc-dhcp-server6")) {
        syslog(LOG_ERR, "Service isc-dhcp-server6.service still running");
        exit(-1);
    }
    if (systemd_isactive("wpa_supplicant")) {
        syslog(LOG_ERR, "Service wpa_supplicant.service still running");
        exit(-1);
    }
}
